package com.pt.common.util;

import com.pt.common.constant.TorrentConstant;
import com.pt.entity.Peer;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.*;
import java.nio.charset.StandardCharsets;

@Slf4j
public class BencodeUtil {
    
    private BencodeUtil() {
        // 私有构造函数，防止实例化
    }
    
    /**
     * 解码种子文件
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> decodeTorrent(byte[] data) {
        log.info("=== BencodeUtil.decodeTorrent被调用 ===");
        log.info("数据长度: {}", (data != null ? data.length : "null"));
        
        try {
            if (data == null || data.length == 0) {
                log.info("数据为空或长度为0");
                return null;
            }
            
            log.info("开始解码...");
            Object result = decode(data);
            if (result instanceof Map) {
                log.info("解码成功");
                return (Map<String, Object>) result;
            } else {
                log.error("解码结果不是Map类型");
                return null;
            }
        } catch (Exception e) {
            log.error("解码时发生未知异常", e);
            return null;
        }
    }
    
    /**
     * 将对象编码为bencode格式
     */
    public static byte[] encodeTorrent(Object obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            encodeTo(obj, baos);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("编码失败", e);
        }
    }
    
    private static void encodeTo(Object obj, OutputStream out) throws IOException {
        if (obj instanceof String) {
            encodeString((String) obj, out);
        } else if (obj instanceof Number) {
            encodeNumber((Number) obj, out);
        } else if (obj instanceof byte[]) {
            encodeBytes((byte[]) obj, out);
        } else if (obj instanceof List) {
            encodeList((List<?>) obj, out);
        } else if (obj instanceof Map) {
            encodeMap((Map<?, ?>) obj, out);
        } else if (obj == null) {
            encodeString("", out);
        } else {
            throw new IOException("Unsupported type: " + obj.getClass());
        }
    }
    
    private static void encodeString(String str, OutputStream out) throws IOException {
        byte[] bytes = str.getBytes(StandardCharsets.ISO_8859_1);
        out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.ISO_8859_1));
        out.write(':');
        out.write(bytes);
    }
    
    private static void encodeNumber(Number n, OutputStream out) throws IOException {
        out.write('i');
        out.write(String.valueOf(n).getBytes(StandardCharsets.ISO_8859_1));
        out.write('e');
    }
    
    private static void encodeBytes(byte[] bytes, OutputStream out) throws IOException {
        out.write(String.valueOf(bytes.length).getBytes(StandardCharsets.ISO_8859_1));
        out.write(':');
        out.write(bytes);
    }
    
    private static void encodeList(List<?> list, OutputStream out) throws IOException {
        out.write('l');
        for (Object item : list) {
            encodeTo(item, out);
        }
        out.write('e');
    }
    
    private static void encodeMap(Map<?, ?> map, OutputStream out) throws IOException {
        // 对键进行排序，确保生成的bencode一致
        TreeMap<String, Object> sortedMap = new TreeMap<>();
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            String key = entry.getKey().toString();
            sortedMap.put(key, entry.getValue());
        }

        out.write('d');
        for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
            encodeString(entry.getKey(), out);
            encodeTo(entry.getValue(), out);
        }
        out.write('e');
    }
    
    /**
     * 解码bencode格式的字节数组
     */
    private static Object decode(byte[] data) {
        try {
            return decode(new DataInputStream(new ByteArrayInputStream(data)));
        } catch (IOException e) {
            throw new RuntimeException("解码失败", e);
        }
    }
    
    private static Object decode(DataInputStream in) throws IOException {
        int ch = in.read();
        if (ch == -1) {
            throw new EOFException();
        }

        switch (ch) {
            case 'i':
                return decodeNumber(in);
            case 'l':
                return decodeList(in);
            case 'd':
                return decodeMap(in);
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                return decodeBytes(in, ch - '0');
            default:
                throw new IOException("Invalid bencode format");
        }
    }
    
    private static Number decodeNumber(DataInputStream in) throws IOException {
        StringBuilder sb = new StringBuilder();
        int ch;
        while ((ch = in.read()) != -1 && ch != 'e') {
            sb.append((char) ch);
        }
        if (ch == -1) {
            throw new EOFException();
        }
        try {
            return Long.parseLong(sb.toString());
        } catch (NumberFormatException e) {
            throw new IOException("Invalid number format", e);
        }
    }
    
    private static byte[] decodeBytes(DataInputStream in, int firstDigit) throws IOException {
        int length = firstDigit;
        int ch;
        while ((ch = in.read()) != -1 && ch != ':') {
            if (ch < '0' || ch > '9') {
                throw new IOException("Invalid string length");
            }
            length = length * 10 + (ch - '0');
        }
        if (ch == -1) {
            throw new EOFException();
        }

        byte[] bytes = new byte[length];
        in.readFully(bytes);
        return bytes;
    }
    
    private static List<Object> decodeList(DataInputStream in) throws IOException {
        List<Object> list = new ArrayList<>();
        while (true) {
            int ch = in.read();
            if (ch == -1) {
                throw new EOFException();
            }
            if (ch == 'e') {
                break;
            }
            // 将读取的字符放回流中
            byte[] oneByte = {(byte) ch};
            ByteArrayInputStream temp = new ByteArrayInputStream(oneByte);
            DataInputStream tempIn = new DataInputStream(new SequenceInputStream(temp, in));
            list.add(decode(tempIn));
        }
        return list;
    }
    
    private static Map<String, Object> decodeMap(DataInputStream in) throws IOException {
        Map<String, Object> map = new LinkedHashMap<>();
        while (true) {
            int ch = in.read();
            if (ch == -1) {
                throw new EOFException();
            }
            if (ch == 'e') {
                break;
            }
            // 将读取的字符放回流中
            byte[] oneByte = {(byte) ch};
            ByteArrayInputStream temp = new ByteArrayInputStream(oneByte);
            DataInputStream tempIn = new DataInputStream(new SequenceInputStream(temp, in));
            byte[] keyBytes = (byte[]) decode(tempIn);
            String key = new String(keyBytes, StandardCharsets.ISO_8859_1);
            Object value = decode(in);
            map.put(key, value);
        }
        return map;
    }
    
    /**
     * 创建错误响应
     */
    public static byte[] createErrorResponse(String message) {
        try {
            Map<String, Object> response = new HashMap<>();
            response.put("failure reason", message);
            return encodeTorrent(response);
        } catch (Exception e) {
            return ("d14:failure reason" + message.length() + ":" + message + "e").getBytes(StandardCharsets.ISO_8859_1);
        }
    }
    
    /**
     * 编码peer响应
     */
    public static byte[] encodePeerResponse(List<Peer> peers, int seeders, int leechers) {
        Map<String, Object> response = new HashMap<>();
        response.put("interval", 1800);
        response.put("min interval", 900);
        response.put("complete", seeders);
        response.put("incomplete", leechers);

        List<Map<String, Object>> peerList = new ArrayList<>();
        for (Peer peer : peers) {
            Map<String, Object> peerInfo = new HashMap<>();
            String ip = peer.getIp();
            // 如果是IPv6地址，转换为IPv4地址
            if (ip.contains(":")) {
                ip = "127.0.0.1";
            }
            peerInfo.put("ip", ip);
            peerInfo.put("port", peer.getPort());
            if (peer.getPeerId() != null) {
                peerInfo.put("peer id", peer.getPeerId());
            }
            peerList.add(peerInfo);
        }
        response.put("peers", peerList);

        try {
            return encodeTorrent(response);
        } catch (RuntimeException e) {
            log.error("编码peer响应失败", e);
            return createErrorResponse("Internal server error");
        }
    }
} 